<?php

function __autoload($class_name) {
	require_once $class_name . '.php';
}


function dump($var)
{
	var_dump($var);
	echo "<br />";
}

class CoverageMethod
{
	public $methodName;
	public $methodLine;

	public function __construct($methodName,$methodLine)
	{
		$this->methodName = $methodName;
		$this->methodLine = $methodLine;
	}
}

class CoverageClass
{
	public $className;
	public $classLine;
	public $lines = array();
	public $methods = array();

	public function __construct($className)
	{
		$this->className = $className;
	}
}


class SweetCoverage
{
	private static $coverageList;

	private static function generateParameterString(array $params)
	{
		$stringParams = array();
		foreach($params as $param)
		{
			$stringParams[] = "\$".$param->getName();
		}
		return $stringParams;
	}

	public static function genRefLine($className,$methodName,$line)
	{
		SweetCoverage::$coverageList[$className]->lines[$line] = 0;
		return "SweetCoverage::coverageRefrenceCount(\"".$className."\",\"".$methodName."\",".$line.");";
	}

	public static function addCode(&$newCode,$className,$methodName,$line)
	{
		$temp = SweetCoverage::genRefLine($className,$methodName,$line);
		$newCode[] = $temp;
		//dump($temp);
	}

	public static function addRefLines($className,$methodName,$code,$method)
	{
		$newCode = array();
		$comment = false;

		$tokens = array("if","foreach","switch","do","while");
		$switchfound = -1;
		for($i=$method->getStartLine();$i<$method->getEndLine();$i++)
		{
			$tcode = trim($code[$i]);
			//dump($tcode);

			$position = strpos($tcode,";");
			if(is_numeric($position))
			{
				//dump("ADDED LINE");
				SweetCoverage::addCode($newCode,$className,$methodName,$i);
				$newCode[] = $code[$i];
				//dump($tcode);
				continue;
			}

			$found = false;
			foreach($tokens as $token)
			{
				$position = strpos($tcode,$token);
				if(is_numeric($position) && $position == 0)
				{
					//dump("--------------Found: ".$token);
					
					if($token == "switch")
					{
						if(strpos($tcode,"{") === false)
						{		
							$switchfound = 0;
						} else {
							$switchfound = 1; 
						}	
					}
					
					//dump("ADDED LINE");
					SweetCoverage::addCode($newCode,$className,$methodName,$i);
					$newCode[] = $code[$i];
					//dump($tcode);
					$found = true;
					break;
				}
			}
			
			if($found)
				continue;

			$position = strpos($tcode,"{");
			if(is_numeric($position))
			{
				if($switchfound === 0)
				{
					$switchfound++;
					$newCode[] = $tcode;
					//dump($tcode);
					continue;
				} else if($switchfound > 0)
				{
					$switchfound++;
				}
				
				$start = substr($tcode,0,$position+1);
				if($start === false)
				$start = "";

				$end = substr($tcode,$position+1);
				if($end === false)
				$end = "";

				//dump($start.SweetCoverage::genRefLine($className,$methodName,$i).$end);
				$newCode[] = $start.SweetCoverage::genRefLine($className,$methodName,$i).$end;
				continue;
			}

			$position = strrpos($tcode,"}");
			if(is_numeric($position))
			{
				if($switchfound === 1)
				{
					$switchfound = -1;
					$newCode[] = $tcode;
					//dump($tcode);
					continue;
				} else if($switchfound != -1)
				{
					$switchfound--;
				}
				$start = substr($tcode,0,$position-1);
				if($start === false)
				$start = "";

				$end = substr($tcode,$position);
				if($end === false)
				$end = "";
					
				//dump($start.SweetCoverage::genRefLine($className,$methodName,$i).$end);
				$newCode[] = $start.SweetCoverage::genRefLine($className,$methodName,$i).$end;
				continue;
			}
			
			$newCode[] = $tcode;
		}
		return $newCode;
	}

	public static function stripComments(&$code)
	{
		//echo htmlspecialchars($code);

		$replace = false;
		$count = 0;
		for($i=0;$i<strlen($code);$i++)
		{
			if($code[$i] == "/" && $code[$i+1] == "*")
			{
				$replace = true;
			} else if($code[$i] == "*" && $code[$i+1] == "/")
			{
				$replace = false;
				$count = 2;
			}


			if($replace || $count > 0)
			{
				if($code[$i] != "\n")
				$code[$i] = " ";
				$count--;
			}
		}

		$replace = false;
		for($i=0;$i<strlen($code);$i++)
		{
			if($code[$i] == "/" && $code[$i+1] == "/")
			{
				$replace = true;
			} else if($code[$i] == "\n")
			$replace = false;

			if($replace)
			$code[$i] = " ";
		}
			

			
			
		//echo htmlspecialchars($code);
	}

	public static function ProcessClass($className)
	{
		$code = file_get_contents($className.".php");
		SweetCoverage::stripComments($code);
		file_put_contents("./temp/".$className.".php",$code);
		include("./temp/".$className.".php");
		//dump($code);

		$code = explode("\n",$code);


		$clazz = new ReflectionClass($className);
		//dump($clazz->getStartLine());
		SweetCoverage::$coverageList[$className]->classLine = $clazz->getStartLine()-1;


		if(!isset(SweetCoverage::$coverageList[$className]))
		{
			SweetCoverage::$coverageList[$className] = new CoverageClass($className);
		}

		$methods = $clazz->getMethods();
		foreach($methods as $method)
		{
			if(!isset(SweetCoverage::$coverageList[$className]->methods[$method->getName()]))
			{
				SweetCoverage::$coverageList[$className]->methods[$method->getName()] = new CoverageMethod($method->getName(),$method->getStartLine()-1);
			}

			$methodArgs = SweetCoverage::generateParameterString($method->getParameters());
			//dump($methodArgs);

			$newCode = SweetCoverage::addRefLines($className,$method->getName(),$code,$method);
			//dump(implode("<br/>",$newCode));

			//dump(implode("<br>",$newCode));
			runkit_method_redefine($className,$method->getName(),implode(",",$methodArgs),implode("",$newCode));
			//dump($newCode);
		}
	}

	public static function coverageRefrenceCount($coverage,$method,$line)
	{
		//dump("Called: ".$coverage." for ".$method." on ".$line);
		SweetCoverage::$coverageList[$coverage]->lines[$line]++;
		SweetCoverage::$coverageList[$coverage]->lines[SweetCoverage::$coverageList[$coverage]->methods[$method]->methodLine]++;
		SweetCoverage::$coverageList[$coverage]->lines[SweetCoverage::$coverageList[$coverage]->classLine]++;
	}

	public static function getCoverage($coverage)
	{
		return SweetCoverage::$coverageList[$coverage];
	}

	public static function getAllCoverage()
	{
		return SweetCoverage::$coverageList;
	}
}

SweetCoverage::ProcessClass("A");
SweetCoverage::ProcessClass("B");
SweetCoverage::ProcessClass("C");

$a = new A();
$c = new C();
$a->c = $c;
$a->dostuff(new B());
$a->dostuff(null);
$a->catter(10);
$a->catter(1);

$coverageSet = SweetCoverage::getAllCoverage();


if($coverageSet != null)
{
	echo "<table border='1'>";
	foreach($coverageSet as $coveragekey => $coveragelines)
	{
		//dump($coveragelines->lines);
		echo "<tr><td colspan='3'>Class: ".$coveragekey."</td></tr>";

		$code = explode("\n",file_get_contents($coveragekey.".php"));
		foreach($code as $codekey => $codeline)
		{
			if(isset($coveragelines->lines[$codekey]))
			{
				$count = $coveragelines->lines[$codekey];
				if($count > 0)
				$colour = "#00FF00";
				else
				$colour = "#FF0000";
			} else {
				$count = 0;
				$colour = "#FFFFFF";
			}

			echo "<tr bgcolor='".$colour."'><td>".$codekey."</td><td>".$count."</td><td><pre>".htmlspecialchars($codeline)."</pre></td></tr>";
		}
	}
}

echo "</table>";

?>


